#!/bin/bash

set -eo pipefail
trap 'echo "ERROR at line $LINENO"; exit 1' ERR

# 定义路径变量
OPENVPN_DIR="/etc/openvpn"
SERVER_DIR="${OPENVPN_DIR}/server"
CLIENT_DIR="${OPENVPN_DIR}/client"
CONFIG_FILE="${OPENVPN_DIR}/.install_settings"

if [ "$(id -u)" != "0" ]; then
    echo "错误：必须使用root权限运行此脚本。"
    exit 1
fi

############################# 安装OpenVPN #############################
install_openvpn() {
    #检查openvpn是否已安装
    if [ -d "${SERVER_DIR}" ]; then
        echo "OpenVPN已安装，请先清理 /etc/openvpn 并重新运行此脚本。"
        exit 1
    fi

    if getent group nogroup >/dev/null; then
        echo "组 nogroup 已存在"
    else
        echo "创建组 nogroup"
        groupadd nogroup
    fi
    # 安装依赖
    if [ -x "$(command -v apt-get)" ]; then
        apt-get update 
        apt-get install -y openvpn easy-rsa curl expect
    elif [ -x "$(command -v yum)" ]; then
        yum install -y epel-release
        yum install -y openvpn easy-rsa curl expect
    fi
    local EASY_RSA_SOURCE="/usr/share/easy-rsa"
    if [ -z "${EASY_RSA_SOURCE}" ]; then
        echo "无法找到 easy-rsa 安装路径"
        exit 1
    fi
    # 配置参数
    echo "请输入OpenVPN配置："
    read -p "协议 (udp/tcp) [默认udp]: " PROTO
    PROTO=${PROTO:-udp}
    read -p "端口 [默认1194]: " PORT
    PORT=${PORT:-1194}
    read -p "监听IP [默认0.0.0.0]: " LISTEN_IP
    LISTEN_IP=${LISTEN_IP:-0.0.0.0}
    read -p "DNS服务器 (空格分隔，如8.8.8.8 8.8.4.4) [默认223.5.5.5]: " DNS_SERVERS
    DNS_SERVERS=${DNS_SERVERS:-"223.5.5.5"}
    PUBLIC_IP=$(curl -s icanhazip.com)
    read -p "服务器公网IP/域名 [自动获取: ${PUBLIC_IP}]: " CUSTOM_IP
    PUBLIC_IP=${CUSTOM_IP:-${PUBLIC_IP}}

    # 保存配置
    {
        echo "PROTO=${PROTO}"
        echo "PORT=${PORT}"
        echo "LISTEN_IP=${LISTEN_IP}"
        echo "DNS_SERVERS='${DNS_SERVERS}'"
        echo "PUBLIC_IP=${PUBLIC_IP}"
        echo "SERVER_DIR=${SERVER_DIR}"
        echo "CLIENT_DIR=${CLIENT_DIR}"
    } > "${CONFIG_FILE}"

    # 创建必要的目录
    mkdir -p "${SERVER_DIR}" "${CLIENT_DIR}"

    # 初始化Easy-RSA
    EASY_RSA="${SERVER_DIR}/easy-rsa"
    mkdir -p "$EASY_RSA"
    cp -r "${EASY_RSA_SOURCE}"/* "$EASY_RSA"/

    # 生成证书
    mkdir -p pki/issued pki/private
    EASY_RSA_PATH=$(find "${SERVER_DIR}/easy-rsa" -name easyrsa -type f -print -quit)

    if [ -z "${EASY_RSA_PATH}" ]; then
        echo "错误：找不到 easyrsa 可执行文件"
    fi

    cd "$(dirname "${EASY_RSA_PATH}")"
    ./easyrsa --batch init-pki
    ./easyrsa --batch build-ca nopass
    ./easyrsa --batch build-server-full server nopass
    ./easyrsa --batch gen-dh
    openvpn --genkey secret pki/ta.key

    # 复制文件到正确位置
    cp pki/ca.crt pki/private/server.key pki/issued/server.crt \
       pki/dh.pem pki/ta.key "${SERVER_DIR}"/

    # 创建服务端配置
    cat > "${SERVER_DIR}/server.conf" <<EOF
local ${LISTEN_IP}
port ${PORT}
proto ${PROTO}
dev tun
ca ${SERVER_DIR}/ca.crt
cert ${SERVER_DIR}/server.crt
key ${SERVER_DIR}/server.key
dh ${SERVER_DIR}/dh.pem
tls-auth ${SERVER_DIR}/ta.key 0
ifconfig-pool-persist ${SERVER_DIR}/ipp.txt
status ${SERVER_DIR}/openvpn-status.log
auth-user-pass-verify "/bin/bash ${SERVER_DIR}/check-user.sh" via-file
server 10.8.0.0 255.255.255.0
push "route 10.8.0.0 255.255.255.0"
push "dhcp-option DNS 223.5.5.5"
client-to-client
keepalive 10 120
cipher AES-256-CBC
auth SHA256
persist-key
persist-tun
verb 3
script-security 3
username-as-common-name
duplicate-cn
EOF

    for dns in $DNS_SERVERS; do
        echo "push \"dhcp-option DNS $dns\"" >> "${SERVER_DIR}/server.conf"
    done

    # 创建用户验证
    cat > "${SERVER_DIR}/check-user.sh" <<'EOF'
#!/bin/sh
###########################################################
# checkpsw.sh (C) 2004 Mathias Sundman <mathias@openvpn.se>
#
# This script will authenticate Openvpn users against
# a plain text file. The passfile should simply contain
# one row per user with the username first followed by
# one or more space(s) or tab(s) and then the password.
PASSFILE="/etc/openvpn/server/psw-file"
LOG_FILE="/tmp/openvpn-password.log"
TIME_STAMP=`date "+%Y-%m-%d %T"`
###########################################################
username=$(head -1 "$1")
password=$(tail -1 "$1") 
if [ ! -r "${PASSFILE}" ]; then
        echo "${TIME_STAMP}: Could not open password file \"${PASSFILE}\" for reading." >> ${LOG_FILE}
        exit 1
fi

CORRECT_PASSWORD=`awk '!/^;/&&!/^#/&&$1=="'${username}'"{print $2;exit}' ${PASSFILE}`

if [ "${CORRECT_PASSWORD}" = "" ]; then
        echo "${TIME_STAMP}: User does not exist: username=\"${username}\", password=\"${password}\"." >> ${LOG_FILE}
        exit 1
fi

if [ "${password}" = "${CORRECT_PASSWORD}" ]; then
        echo "${TIME_STAMP}: Successful authentication: username=\"${username}\"." >> ${LOG_FILE}
        exit 0
fi

echo "${TIME_STAMP}: Incorrect password: username=\"${username}\", password=\"${password}\"." >> ${LOG_FILE}
exit 1
EOF
    chmod +x "${SERVER_DIR}/check-user.sh"
    touch "${SERVER_DIR}/psw-file"
    chmod 600 "${SERVER_DIR}/psw-file"

    # 网络配置
    echo 'net.ipv4.ip_forward = 1' >> /etc/sysctl.conf
    sysctl -p

    if ! iptables -t nat -C POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE 2>/dev/null; then
        iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE
    fi
    iptables-save > /etc/iptables.rules

    # 启动服务
    systemctl enable --now openvpn-server@server
    echo "OpenVPN安装完成！"
}

############################# 添加用户 ###############################
add_user() {
    if [ $# -lt 2 ]; then
        read -p "用户名: " username
        read -sp "密码: " password
        echo
    else
        username=$1
        password=$2
    fi

    # 检查用户是否已存在
    if grep -q "^${username} " "${SERVER_DIR}/psw-file"; then
        echo "错误：用户 ${username} 已存在！"
        exit 1
    fi

    # 添加用户密码
    echo "${username} ${password}" >> "${SERVER_DIR}/psw-file"
    chmod 600 "${SERVER_DIR}/psw-file"

    # 查找 easyrsa 路径
    EASY_RSA_PATH=$(find "${SERVER_DIR}/easy-rsa" -name easyrsa -type f -print -quit)
    [ -z "${EASY_RSA_PATH}" ] && { echo "错误：找不到 easyrsa 可执行文件"; exit 1; }

    # 进入 easyrsa 目录
    cd "$(dirname "${EASY_RSA_PATH}")"

    # 清理可能存在的旧文件
    rm -f "pki/reqs/${username}.req" "pki/private/${username}.key" "pki/issued/${username}.crt" 2>/dev/null

    # 生成客户端证书（自动应答）
    #./easyrsa build-client-full "${username}" nopass
    yes "yes" | ./easyrsa build-client-full "${username}" nopass

    # 检查证书是否生成成功
    [ ! -f "pki/issued/${username}.crt" ] && { echo "错误：证书生成失败"; exit 1; }

    # 创建客户端配置目录
    mkdir -p "${CLIENT_DIR}/${username}"

    PORT=$(grep -oP 'port \K\d+' "${SERVER_DIR}/server.conf")
    PROTO=$(grep -oP 'proto \K\w+' "${SERVER_DIR}/server.conf")
    PUBLIC_IP=$(curl -s icanhazip.com)
    # 生成客户端配置文件
    {
        echo "client"
        echo "dev tun"
        echo "proto ${PROTO}"
        echo "remote ${PUBLIC_IP} ${PORT}"
        echo "resolv-retry infinite"
        echo "nobind"
        echo "persist-key"
        echo "persist-tun"
        echo "auth-user-pass"
        echo "remote-cert-tls server"
        echo "cipher AES-256-CBC"
        echo "auth SHA256"
        echo "key-direction 1"
        echo "auth-nocache"
        echo "verb 3"
        echo "<ca>"
        cat "${SERVER_DIR}/ca.crt"
        echo "</ca>"
        echo "<cert>"
        awk '/BEGIN/,/END/' "pki/issued/${username}.crt"
        echo "</cert>"
        echo "<key>"
        cat "pki/private/${username}.key"
        echo "</key>"
        echo "<tls-auth>"
        cat "${SERVER_DIR}/ta.key"
        echo "</tls-auth>"
    } > "${CLIENT_DIR}/${username}/${username}.ovpn"

    echo "用户添加成功！"
    echo "配置文件路径: ${CLIENT_DIR}/${username}/${username}.ovpn"
}

############################## 主程序 ###############################

case "$1" in
    install)
        install_openvpn
        ;;
    adduser)
        shift
        add_user "$@"
        ;;
    *)
        echo "用法: $0 [install|adduser 用户名 密码]"
        exit 1
        ;;
esac